/*
    HMR 热模块替换
    模块打包 不会打包所有模块
    css:可以使用HMR
    js:默认不使用HMR
    html：默认不使用HMR
    修改 entry将html文件引入
*/
/*
    缓存:（hash|chunkhash|contenthash）
        babel缓存
            cacheDirectory:true
            --》 让第二次打包构建速度更快
        文件资源缓存
            hash:每次webpack构建时会生成一个唯一的hash值
            问题：因为js和css同时使用一个hash值
            如果重新打包，会导致所有缓存失效
            chunkhash：根据chunk生成的hash值，如果打包来源于同一个chunk那么hash值一样
            问题：js和css的hash值还是一样的
            因为css是在js中呗引入的 ，所以同属于一个chunk
            contenthash:根据文件的内容生成hash值。不同文件hash值一定不一样
            --》让代码上线运行缓存更好的使用
*/
/*
    tree shaking 去除无用代码
        前提： 1.必须使用es6模块化 2.开启production环境
        作用: 减少代码体积
        package.json中配置
            "sideEffects":false 所有代码都没有副作用 (全部可以tree shaking)
            问题 ： 可能会把css / @babel/polyfill 文件被干掉
            "sideEffects":["*.css", "*.less"]
*/
const webpack = require('webpack')
const path = require('path')
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
// css处理以及打包时生成css文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// 压缩css  
const optimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const addAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')
// 用于minimizer
const TerserWebpackPlugin = require('terser-webpack-plugin')
// 设置nodejs环境变量
// process.env.NODE_ENV = 'development'
// MiniCssExtractPlugin复用
const commonCssLoader=[
    MiniCssExtractPlugin.loader,
    'css-loader',
    {
        loader:"postcss-loader",
        ident: 'postcss',
        options:{
            postcssOptions: {
                //或者将插件引入写在单独的配置js中
                config: './postcss.config.js',
                // plugins: () => [
                //     require('postcss-preset-env')()
                // ]
            }
        }
    }
]
module.exports = {
    /*
        devServer
        开发环境的配置
        contentBase -->运行代码的目录
        watchContentBase --> 监视contentBase 下的所有文件，一旦文件变化就会reload true/false
        watchOption --> 忽略文件
        compress --> 启动gzip压缩增加速率
        port --> 运行端口号
        host --> 域名
        hot --> HMR热更新功能 true/false
        clientLogLevel --> 不显示启动服务器的日志信息 none
        quiet --> 除了一些基本启动的信息之外，其他内容都不要显示 true/false
        overlay --> 如果出现错误不要全屏提示
        proxy --> 服务器代理
    */
    devServer:{
        contentBase:resolve(__dirname,'build'),
        compress:true,
        port:3009,
        hot:true,
        clientLogLevel:'none',
        quiet:true,
        watchOption:{
            // 忽略文件
            ignored:/node_modules/
        },
        overlay:false,
        proxy:{
            '/api':{
                target:'http://xxxx',
                // 发送请求时 请求路径重写:将/api/xx -->/xx 去掉api
                pathRewrite:{
                    '^/api':''
                }
            }
        }
    },
    /*
        entry:入口起点
        1.string --> './src/index.js'
            单入口
            打包形成一个chunk，输出一个bundle文件
            此时的chunk默认名称是main
        2.array --> ['./src/index.js','./src/xx.js'],
            多入口
            所有的入口文件最终只会形成一个chunk，输出出去也只有一个bundle文件
            -->只有在HMR功能中让html热更新生效
        3.object
            多入口
            有几个入口文件就形成几个chunk，输出几个bundle文件
            此时chunk的名称是key

            --> 特殊用法 object中也可以数组格式放入 数组中的文件会放入对应的key bundle文件中 
    */
    entry: {
        index:"./src/index.js"
    },
    output: {
        // 文件名称
        filename: "./js/[name].[contenthash:10].js",
        // 输出文件目录
        path: resolve(__dirname,"build"),
        // 所有资源引入公共路径前缀 --> 'img/a.jpg' --> '/img/a.jpg'
        publicPath:'/',
        // 非入口chunk的名称 (import 引入的js文件 )
        chunkFilename:'./js/[name].[contenthash:10]_chunk.js',
        // 整个库向外暴露的变量名
        // library:'[name]',
        // 变量名添加到那个环境上
        // libraryTarget:'window' 
        // libraryTarget:'global' 
        // libraryTarget:'commonjs' 
    },
    /*
        optimization
        1-可以将node_module中代码单独打包一个chunk最终输出
        2-自动分析多入口chunk中，有没有公共的文件。如果有会打包成单独一个chunk
        optimization:{
            spliteChunks:{
                chunks:'all',
                以下配置如果不写则有默认值
                minSize:30*1024 分割chunk最小为30kb
                maxSize:0 最大没有限制
                minChunks:1 要提取的chunk最少被引用1次
                maxAsyncRequest:5 按需加载时并行加载文件的最大数量
                maxInitialRequest:3 入口js文件最大并行请求数量
                automaticNameDelimiter:'~' 名称链接符
                name:true  可以使用命名规则
                分割chunk的组
                cacheGroup:{
                    node_modules 文件会被打爆到venders 组的chunk中 -->vendors~xxx.js
                    vendors:{
                        test:/[\\/]node_modules[\\/]/
                        priority:-10 优先级为-10
                    },
                    default:{
                        要提取的chunk最少被引用2次
                        minChunks:2,
                        priority:-30,
                        如果当前要打包的模块，和之前已经被提取的模块是同一个 就会复用。而不是重新打包
                        reuseExistingChunk:true
                    }
                }
            }
            将当前模块的记录模块的hash单独打包为一个文件runtime
            // 解决 修改a文件导致b文件的contenthash变化
            runtimeChunk:{
                name:entrypoint =>`runtime-${entrypoint.name}`
            }
        }
    */
   optimization:{
        // spliteChunks:{
        //     chunks:'all',
            
        // },
        runtimeChunk:{
            name:entrypoint =>`runtime-${entrypoint.name}`
        },
        // 配置生产环境的压缩方案 js和css
        minimizer:[
            new TerserWebpackPlugin({
                // 开启缓存
                // cache:true,
                // 开启多进程打包
                parallel:true,
                terserOptions: {
                    ecma: undefined,
                    warnings: false,
                    parse: {},
                    compress: {
                      drop_console: true,
                      drop_debugger: false,
                      pure_funcs: ['console.log'], // 移除console
                    },
                },
                // 启动source-map
                // sourceMap:true,
            })
        ]
    },
    /*
        module
        为webpack下的文件做兼容与优化操作
        exclude --> 排除某些目录下的文件
        include --> 只检查某些目录下的文件
        loader --> arrary|string 多个loader时 使用 use:[loader,loader]
        enforce --> 'pre'优先执行 'post'延后执行 不写就是中间执行
        options --> loader下的配置
        oneOf --> 以下配置只会执行一个
    */
    module:{
        rules:[
            // 语法检查 eslint-loader eslint
            // package.json中eslintConfig中设置
            //  aribnb --> eslint-config-airbnb-base eslint eslint-plugin-import
            // js想忽略 输入 eslint-disable-next-line
            {
                test:/\.js$/,
                // 刨除node包
                exclude:'/node-modules/',
                enforce:'pre',
                loader:"eslint-loader",
                include:[path.resolve(__dirname,'src')],
                options:{
                    // 自动修复eslint 的错误
                    fix:true
                }
            },
            {
                // 以下loader只会匹配一个
                // 注意：不能有两个配置处理同一种类型的文件
                oneOf:[
                    // css loader
                    {
                        test:/\.css$/,
                        use:[
                            ...commonCssLoader
                        ],
                    },
                    // less loader
                    {
                        test:/\.less$/,
                        use:[
                            ...commonCssLoader,
                            // 需要less-loader 和less
                            'less-loader',

                        ]
                    },
                    // scss loader
                    {
                        test:/\.scss$/,
                        use:[
                            ...commonCssLoader,
                            'sass-loader'
                        ]
                    },
                    // 图片资源
                    {
                        test:/\.(png|jpe?g|gif|svg)(\?.*)?$/,
                        // 需要下载url-loader file-loader
                        loader:'url-loader',
                        include: [resolve('static'),resolve('src')],
                        options:{
                            // 小于8kb被base64处理
                            limit:8*1024,
                            // 关闭es6模块使用commonjs
                            // esModule:false
                            // 图片设置名称
                            name:"njl_[hash:10].[ext]",
                            outputPath:"images"
                        }
                    },
                    {
                        test:/\.html$/,
                        // 处理html中img引入问题
                        loader:'html-loader'
                    },
                    // 打包其他文件
                    {
                        // 刨除
                        exclude:/\.(css|js|html|scss|less|png|jpe?g|gif|svg)$/,
                        loader:'file-loader',
                        options:{
                            name:"other_[hash:10].[ext]",
                            outputPath:"media"
                        }
                    },
                    
                    // js兼容性处理
                    // babel-loader @babel/preset-env @babel/core 基本js的兼容 如promise等不能转换
                    // js全部兼容性处理 --> @babel/polyfill 直接在文件中引用
                    // 按需加载兼容 -->core-js 用这个 就不介意用 @babel/polyfill
                    {
                        test:/\.js$/,
                        
                        exclude:"/node_modules/",
                        use:[
                            /*  
                                thread-loader
                                开启多线程打包
                                进程启动大概为600MS,进程通信有开销
                                只有工作耗时长，才需要打包
                            */
                            {
                                loader:'thread-loader',
                                options:{
                                    worker:2 // 设置进程数
                                }
                            },
                            {
                                loader:"babel-loader",
                                options:{
                                    // 预设：指示做什么样的兼容性处理
                                    presets:[
                                        [
                                            '@babel/preset-env',
                                            // core.js目前与MiniCssExtractPlugin有冲突
                                            // {
                                            //     // 按需加载
                                            //     useBuiltIns:'usage',
                                            //     // 指定core-js版本
                                            //     corejs:{
                                            //         version:3
                                            //     },
                                            //     // 指定兼容性做到哪个版本浏览器
                                            //     targets:{
                                            //         chrome:"60",
                                            //         firefox:"60",
                                            //         ie:"9",
                                            //         safari:"10",
                                            //         edge:"17"
                                            //     }
                                            // }
                                        ]
                                    ],
                                    // 开启babel缓存
                                    // 第二次构建时，会读取之前的缓存
                                    cacheDirectory:true
                                }
                            }
                        ],
                        
                    },
                ]
            }
            

        ]
    },
    plugins:[
        // 默认创建空的HTML 引入打包输出的资源
        new HtmlWebpackPlugin({
            // 模板html
            template:"./public/index.html",
            minify:{
                // 移除空格
                collapseWhitespace:true,
                // 移除注释
                removeComments:true
            }
        }),
        new MiniCssExtractPlugin({
            filename:"css/main.[contenthahs:10].css"
        }),
        new optimizeCssAssetsWebpackPlugin(),
        /*
            通知webpack那些库不参与打包 使用时名称改变
            new webpack.DllReferencePlugin({
                manifest:resolve(__dirname,'dll/manifest.json')
            }), 
            将文件打包输出去并在html中自动引入资源
            new addAssetHtmlWebpackPlugin({
                filepath:resolve(__dirname,'dll/jquery')
            })
        */
    ],
    
    devtool:"source-map",
    /*
      忽略一下的东西被打包
    */
    // externals:{
    //     jquery:"jquery"
    // },
    mode:"development",
    /*
        resolve
        解析模块的规则
        alias --> 配置解析模块的路径别名
        extensions --> 配置省略文件路径的后缀名 ['.js','.json','css','vue','jsx']
        modules -->  告诉webpack解析模块是去哪个目录找文件
    */
    resolve:{
        alias:{
            $css:resolve(__dirname,'src/css'),
            '@':resolve(__dirname,'src')
        },
        extensions:['.js','.json','css'],
        modules:['node_modules']
    }
};
/*
    source-map 一种提供源代码到构建后代码映射
    [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
    source-map 外部
        错误代码准确信息和源代码错误位置
    inline-source-map 内联
        错误代码准确信息和源代码错误位置
        只生成一个内联source-map
    hidden-source-map
        错误代码错误原因 没有错误位置不能追踪源代码错误位置，只能看到构建后的错误代码位置（隐藏源代码）
    eval-source-map
        每一个文件都会生成对应的source-map 都在eval
    nosources-source-map 外部
        错误代码准确信息，但是没有任何源代码信息 （隐藏源代码）
    cheap-source-map 外部
        错误代码准确信息和源代码错误位置
        只能精确到行 列不行
    cheap--module-source-map 外部

*/